package main

import (
	"fmt"
	"net/http"
	"proxy/api"
	"proxy/meta"
	"proxy/recorder"
	"time"
)

type ApiHandler struct {
	state *ProxyState
}

func NewApiHandler(state *ProxyState) *ApiHandler {
	return &ApiHandler{
		state: state,
	}
}

func (h *ApiHandler) OnStatusReq() *api.Response {
	status := h.state.meta.Status()

	return &api.Response{
		Op:   "rsp",
		Meta: status,
	}
}

func (h *ApiHandler) OnCallReq(req *api.Request) *api.Response {
	var selectedBackend *meta.Backend

	conf := h.state.meta.GetConf(req.CtxId)

	//if conf != nil {
	//	selectedBackend = h.mostUsedBackend(conf)
	//}

	if selectedBackend == nil {
		backends := h.state.meta.BackendAll()
		luBackendId := -1
		luBackendCount := 0
	findLeastUsedBackend:
		for _, backend := range backends {
			if backend == nil {
				continue findLeastUsedBackend
			}

			count := backend.RtcCount()

			if !backend.IsConnected() {
				continue findLeastUsedBackend
			}

			if luBackendId != -1 && count > luBackendCount {
				continue findLeastUsedBackend
			}

			luBackendId = backend.Id
			luBackendCount = count
			selectedBackend = backend

			log.Infof("new call, selected backend '%d', has %d rtc ", luBackendId, luBackendCount)
		}
	}

	if selectedBackend == nil {
		log.Warningf("could not create call, backend not available")
		rsp := &api.Response{
			Op:      "rsp",
			Status:  http.StatusServiceUnavailable,
			Message: "backend not available",
		}
		return rsp
	}

	id, err := selectedBackend.NewRtcId()
	if err != nil {
		log.Warningf("[%s:%s], could not allocate rtc id, %s", req.TsxId, req.Op, err)
		rsp := &api.Response{
			Op:      "rsp",
			Status:  http.StatusInternalServerError,
			Message: "could not allocate rtc id",
		}
		return rsp
	}

	log.Infof("[%s:%s], created rtc %d, minVideoRate: %v, maxVideoRate: %v",
		req.TsxId, req.Op, id.Int(), req.GetMinVideoBitrate(), req.GetMaxVideoBitrate())

	selectedBackend.NewRtc(id.RtcId)

	if conf == nil {
		conf, err = h.state.meta.NewConf(req.CtxId, req.ConfType)
	}

	if conf != nil {
		conf.AddRtc(id)
	}

	req.RtcId = fmt.Sprintf("%d", id.Int())

	rsp := h.sendRequestAndWaitForResponse(selectedBackend.Id, req)

	//log.Noticef("[%s:%s], got response status %d", req.TsxId, req.Op, rsp.Status)

	if rsp.Status != 0 {
		log.Noticef("[%s:%s], could not create rtc %d", req.TsxId, req.Op, id.Int())
		return rsp
	}

	if req.ConfType == "audio-mixer" {
		if rsp.LinkUpdate { // 第一个Rtc
			backendIdList := conf.ListBackend()
			log.Infof("mixer conf '%s' link update: link addr '%s', notify backends %v",
				req.CtxId, rsp.LinkAddr, backendIdList)

			for _, backendId := range backendIdList {
				if backendId == selectedBackend.Id {
					continue
				}
				joinReq := &api.Request{
					Op:       api.OpJoinMixer,
					CtxId:    req.CtxId,
					LinkAddr: rsp.LinkAddr,
				}

				rpubRsp := h.sendRequestAndWaitForResponse(backendId, joinReq)

				log.Infof("backend '%d' joined conf '%s', status %d", backendId, rsp.LinkAddr, rpubRsp.Status)
			}
		} else {
			log.Infof("mixer conf '%s' link no update", req.CtxId)
		}
	}

	return rsp
}

func (h *ApiHandler) mostUsedBackend(conf *meta.Conf) *meta.Backend {
	backendId := -1

	rtcCount := 0

	confRtcCountByBackend := conf.CountRtcByBackend()

	for id, confRtcCount := range confRtcCountByBackend {
		backend := h.state.meta.GetBackend(id)
		if backend == nil {
			continue
		} else if !backend.IsConnected() {
			continue
		} else if backendRtcCount := backend.RtcCount(); backendRtcCount > optMaxRtc {
			continue
		} else if confRtcCount > rtcCount {
			backendId = id
			rtcCount = confRtcCount
		}
	}

	return h.state.meta.GetBackend(backendId)
}

func (h *ApiHandler) OnAnswerReq(req *api.Request) *api.Response {
	id, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, req.RtcId)
	}
	return h.sendRequestAndWaitForResponse(id.BackendId, req)
}

func (h *ApiHandler) OnTerminateReq(req *api.Request) *api.Response {

	id, err := meta.ParseRtcId(req.RtcId)

	if err != nil {
		return badRequest(req, req.RtcId)
	}

	rsp := h.sendRequestAndWaitForResponse(id.BackendId, req)

	conf := h.state.meta.GetConf(req.CtxId)

	if conf != nil {
		if conf.DelRtc(id) {
			h.state.meta.DelConf(req.CtxId)
		}
	}

	return rsp
}

func (h *ApiHandler) OnCandidateReq(req *api.Request) *api.Response {
	id, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, req.RtcId)
	}
	return h.sendRequestAndWaitForResponse(id.BackendId, req)
}

func (h *ApiHandler) OnSubscribeReq(req *api.Request) *api.Response {
	subId, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, req.RtcId)
	}

	pubId, err := meta.ParseRtcId(req.PubRtcId)
	if err != nil {
		return badRequest(req, req.PubRtcId)
	}

	if subId.BackendId == pubId.BackendId {
		return h.subOneBackend(subId, req)
	}

	if !req.Video && !req.Audio {
		req.Video = true
		req.Audio = true
	}

	return h.subTwoBackend(pubId, subId, req)
}

func (h *ApiHandler) subOneBackend(id meta.RtcId, req *api.Request) *api.Response {
	return h.sendRequestAndWaitForResponse(id.BackendId, req)
}

func (h *ApiHandler) subTwoBackend(pubId meta.RtcId, subId meta.RtcId, req *api.Request) *api.Response {
	subReq := &api.Request{
		Op:       api.OpSubscribeRemote,
		TsxId:    req.TsxId,
		SubRtcId: req.RtcId,
		PubRtcId: req.PubRtcId,
		Audio:    req.Audio,
		Video:    req.Video,
	}

	// audio/video ssrcs are fixed, otherwise subC should carry those values
	if req.Audio {
		subReq.PeerAudioSSRCs = req.PeerAudioSSRCs //[]uint32 {5555}
	}
	if req.Video {
		subReq.PeerVideoSSRCs = req.PeerVideoSSRCs //[]uint32 {7777,8888}
	}

	subRsp := h.sendRequestAndWaitForResponse(subId.BackendId, subReq)

	if subRsp.Status != 0 {
		log.Warningf("[%s:%s], rtc '%s' subscribe error: %s", req.TsxId, req.Op, subReq.SubRtcId, subRsp.Message)
		return &api.Response{
			Op:      api.OpResponse,
			Status:  subRsp.Status,
			Message: subRsp.Message,
			TsxId:   subRsp.TsxId,
		}
	}

	log.Infof("[%s:%s], rtc '%s' subscribe success", req.TsxId, req.Op, subReq.SubRtcId)

	if !subRsp.LinkUpdate {
		log.Infof("[%s:%s], reuse link", req.TsxId, req.Op)
		return &api.Response{
			Op:      api.OpResponse,
			Status:  subRsp.Status,
			Message: subRsp.Message,
			TsxId:   subRsp.TsxId,
		}
	}

	log.Infof("[%s:%s], create link", req.TsxId, req.Op)

	pubReq := &api.Request{
		Op:       api.OpPublishRemote,
		TsxId:    req.TsxId,
		PubRtcId: req.PubRtcId,
		SubRtcId: req.RtcId,
		Audio:    subRsp.LinkAudio,
		Video:    subRsp.LinkVideo,
		LinkAddr: subRsp.LinkAddr,

		PeerVideoSSRCs: subRsp.AudioSSRCs,
		PeerAudioSSRCs: subRsp.VideoSSRCs,
	}

	pubRsp := h.sendRequestAndWaitForResponse(pubId.BackendId, pubReq)
	if pubRsp.Status != 0 {
		log.Warningf("[%s:%s], rtc '%s' publish error: %s", req.TsxId, req.Op, req.PubRtcId, pubRsp.Message)
		return &api.Response{
			Op:      api.OpResponse,
			Status:  pubRsp.Status,
			Message: pubRsp.Message,
			TsxId:   pubRsp.TsxId,
		}
	}

	log.Infof("[%s:%s], rtc '%s' publish succcess", req.TsxId, req.Op, req.PubRtcId)

	return &api.Response{
		Op:      api.OpResponse,
		Status:  0,
		Message: "",
		TsxId:   req.TsxId,
	}
}

func (h *ApiHandler) OnUnSubscribeReq(req *api.Request) *api.Response {
	subId, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, req.RtcId)
	}

	pubId, err := meta.ParseRtcId(req.PubRtcId)
	if err != nil {
		return badRequest(req, req.PubRtcId)
	}

	if subId.BackendId == pubId.BackendId {
		return h.unSubOneBackend(subId, req)
	}
	return h.unSubTwoBackend(pubId, subId, req)
}

func (h *ApiHandler) unSubOneBackend(id meta.RtcId, req *api.Request) *api.Response {
	return h.sendRequestAndWaitForResponse(id.BackendId, req)
}

func (h *ApiHandler) unSubTwoBackend(pubId meta.RtcId, subId meta.RtcId, req *api.Request) *api.Response {
	usubReq := &api.Request{
		Op:       api.OpUnSubscribeRemote,
		TsxId:    req.TsxId,
		SubRtcId: req.RtcId,
		PubRtcId: req.PubRtcId,
	}

	usubRsp := h.sendRequestAndWaitForResponse(subId.BackendId, usubReq)
	if usubRsp.Status != 0 {
		log.Warningf("[%s:%s], rtc '%s' un-subscribe response status %d", req.TsxId, req.Op, req.SubRtcId, usubRsp.Status)
		return &api.Response{
			Op:      api.OpResponse,
			Status:  usubRsp.Status,
			Message: usubRsp.Message,
			TsxId:   usubRsp.TsxId,
		}
	}

	log.Infof("[%s:%s], rtc '%s' un-subscribe success", req.TsxId, req.Op, req.SubRtcId)

	if !usubRsp.LinkUpdate {
		log.Infof("[%s:%s], keep link", req.TsxId, req.Op)
		return &api.Response{
			Op:      api.OpResponse,
			Status:  usubRsp.Status,
			Message: usubRsp.Message,
			TsxId:   usubRsp.TsxId,
		}
	}

	log.Infof("[%s:%s], close link", req.TsxId, req.Op)

	upubReq := &api.Request{
		Op:       api.OpUnPublishRemote,
		TsxId:    req.TsxId,
		PubRtcId: req.PubRtcId,
		SubRtcId: req.RtcId,
	}

	upubRsp := h.sendRequestAndWaitForResponse(pubId.BackendId, upubReq)
	if upubRsp.Status != 0 {
		log.Warningf("[%s:%s], rtc '%s' un-publish error: %s", req.TsxId, req.Op, req.PubRtcId, upubRsp.Message)
		return &api.Response{
			Op:      api.OpResponse,
			Status:  upubRsp.Status,
			Message: upubRsp.Message,
			TsxId:   upubRsp.TsxId,
		}
	}

	log.Infof("[%s:%s], rtc '%s' un-publish success", req.TsxId, req.Op, req.PubRtcId)

	return &api.Response{
		Op:      api.OpResponse,
		Status:  0,
		Message: "",
		TsxId:   req.TsxId,
	}
}

func (h *ApiHandler) OnPubRemote(req *api.Request) *api.Response {
	if len(req.PubRtcId) == 0 {
		return badRequest(req, "field |pubRtcId| is empty")
	}

	if len(req.SubRtcId) == 0 {
		return badRequest(req, "field |subRtcId| is empty")
	}

	if len(req.PeerAudioSSRCs) == 0 && len(req.PeerVideoSSRCs) == 0 {
		return badRequest(req, "field |peerAudioSSRCs| and |peerVideoSSRCs| "+
			"could not be empty at same time")
	}

	if len(req.LinkAddr) == 0 {
		return badRequest(req, "field |link-addr| is empty")
	}

	// this is a workaround for:
	// xswitch not accept audioSSrc/videoSSrc
	// instead it expect audio/video
	{
		if len(req.PeerAudioSSRCs) != 0 {
			req.Audio = true
		}
		if len(req.PeerVideoSSRCs) != 0 {
			req.Video = true
		}
	}

	pubId, err := meta.ParseRtcId(req.PubRtcId)

	if err != nil {
		return badRequest(req, fmt.Sprintf("requested rtc |%s| not found",
			req.PubRtcId))
	}

	return h.sendRequestAndWaitForResponse(pubId.BackendId, req)
}

func (h *ApiHandler) OnUnPubRemote(req *api.Request) *api.Response {
	if len(req.PubRtcId) == 0 {
		return badRequest(req, "field |pubRtcId| is empty")
	}

	if len(req.SubRtcId) == 0 {
		return badRequest(req, "field |subRtcId| is empty")
	}

	pubId, err := meta.ParseRtcId(req.PubRtcId)

	if err != nil {
		return badRequest(req, fmt.Sprintf("requested rtc |%s| not found",
			req.PubRtcId))
	}

	return h.sendRequestAndWaitForResponse(pubId.BackendId, req)
}

func (h *ApiHandler) OnBootReq(req *api.Request) *api.Response {
	return &api.Response{
		Op:      api.OpResponse,
		Status:  0,
		Message: "",
		TsxId:   req.TsxId,
		Api:     fmt.Sprintf("http://%s/rtc/", optApiAddr),
	}
}

func (h *ApiHandler) OnNewScene(req *api.Request) *api.Response {
	timeout := time.Duration(req.RecTimeout) * time.Millisecond
	scene := recorder.NewScene(req.RecPriority, req.RecSubDir, req.RecType, timeout, req.Ctx)
	sceneId := h.state.recorder.AddScene(scene)
	log.Infof("created scene '%s'", sceneId)
	return &api.Response{
		Op:       api.OpResponse,
		Status:   0,
		Message:  "",
		TsxId:    req.TsxId,
		RecScene: sceneId,
	}
}
func (h *ApiHandler) OnNewSegment(req *api.Request) *api.Response {
	rtcId, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, fmt.Sprintf("could not parse rtc '%s'", req.RtcId))
	}

	scene := h.state.recorder.GetScene(req.RecScene)
	if scene == nil {
		return badRequest(req, fmt.Sprintf("could not find scene '%s'", req.RecScene))
	}

	source := scene.GetOrCreateSource(req.RecSource)

	rsp := h.sendRequestAndWaitForResponse(rtcId.BackendId, req)
	if rsp.Status != 0 {
		log.Warningf("[%s:%s] xswitch response status '%d:%s'", req.TsxId, req.Op, rsp.Status, rsp.Message)
		return rsp
	}

	segment := recorder.NewSegment(time.Now(), req.RecScene, req.RecSource, rsp.RecTlv, req.Audio, req.Video, h.checkRtcAlive(rtcId))

	segmentId := source.AddSegment(segment)

	return &api.Response{
		Op:         api.OpResponse,
		Status:     0,
		Message:    "",
		TsxId:      req.TsxId,
		RecSource:  req.RecSource,
		RecSegment: segmentId,
	}
}

func (h *ApiHandler) OnNewSegment_2(req *api.Request) *api.Response {
	rtcId, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, fmt.Sprintf("could not parse rtc '%s'", req.RtcId))
	}

	scene := h.state.recorder.GetScene(req.RecScene)
	if scene == nil {
		return badRequest(req, fmt.Sprintf("could not find scene '%s'", req.RecScene))
	}
	currentTime := time.Now()
	source := scene.GetOrCreateSource_2(currentTime, req.RecSource, req.AcType)

	rsp := h.sendRequestAndWaitForResponse(rtcId.BackendId, req)
	if rsp.Status != 0 {
		log.Warningf("[%s:%s] xswitch response status '%d:%s'", req.TsxId, req.Op, rsp.Status, rsp.Message)
		return rsp
	}

	segment := recorder.NewSegment(currentTime, req.RecScene, req.RecSource, rsp.RecTlv, req.Audio, req.Video, h.checkRtcAlive(rtcId))
	log.Infof("the segmeng is '%s:%s'", req.RecSource, currentTime)

	segmentId := source.AddSegment_2(segment)

	return &api.Response{
		Op:         api.OpResponse,
		Status:     0,
		Message:    "",
		TsxId:      req.TsxId,
		RecSource:  req.RecSource,
		RecSegment: segmentId,
	}
}

func (h *ApiHandler) checkRtcAlive(rtcId meta.RtcId) func() bool {
	checker := func() bool {
		backend := h.state.meta.GetBackend(rtcId.BackendId)
		if backend == nil {
			return false
		}
		rtc := backend.GetRtc(rtcId.RtcId)
		return rtc != nil
	}
	return checker
}

func (h *ApiHandler) OnDelSegment(req *api.Request) *api.Response {
	scene := h.state.recorder.GetScene(req.RecScene)
	if scene == nil {
		return badRequest(req, fmt.Sprintf("could not find scene '%s'", req.RecScene))
	}

	source := scene.GetSource(req.RecSource)
	if source == nil {
		return badRequest(req, fmt.Sprintf("could not find source '%s/%s'", req.RecScene, req.RecSource))
	}

	source.CloseSegment(req.RecSegment)

	return &api.Response{
		Op:      api.OpResponse,
		Status:  0,
		Message: "",
		TsxId:   req.TsxId,
	}
}

func (h *ApiHandler) OnDelSegment_2(req *api.Request) *api.Response {
	scene := h.state.recorder.GetScene(req.RecScene)
	if scene == nil {
		return badRequest(req, fmt.Sprintf("could not find scene '%s'", req.RecScene))
	}
	currentTime := time.Now()
	if req.AcType == 2 { // 手动结束录制
		scene.EndRec(currentTime)
	} else {
		scene.CreateAndCopySource_2(currentTime, req.RecSource)
	}
	return &api.Response{
		Op:      api.OpResponse,
		Status:  0,
		Message: "",
		TsxId:   req.TsxId,
	}
}
func (h *ApiHandler) OnDelScene(req *api.Request) *api.Response {
	scene := h.state.recorder.DelScene(req.RecScene)

	if scene == nil {
		return badRequest(req, fmt.Sprintf("could not find scene '%s'", req.RecScene))
	}

	log.Infof("[%s:%s] deleted scene '%s'", req.TsxId, req.Op, req.RecScene)

	if err := scene.CreateJobFile(optJobDir); err != nil {

		log.Errorf("[%s:%s] could not create job from scene '%s'", req.TsxId, req.Op, req.RecSource)

		return &api.Response{
			Op:      api.OpResponse,
			Status:  500,
			Message: "could not create job",
			TsxId:   req.TsxId,
		}
	}

	return &api.Response{
		Op:      api.OpResponse,
		Status:  0,
		Message: "",
		TsxId:   req.TsxId,
	}
}
func (h *ApiHandler) OnRtcUpd(req *api.Request) *api.Response {
	id, err := meta.ParseRtcId(req.RtcId)
	if err != nil {
		return badRequest(req, req.RtcId)
	}
	rsp := h.sendRequestAndWaitForResponse(id.BackendId, req)
	return rsp
}

func (h *ApiHandler) sendRequestAndWaitForResponse(backend int, req *api.Request) *api.Response {
	rsp, err := h.state.backend.ForwardReq(backend, req)
	if err != nil {
		log.Warningf("[%s:%s], forward to backend %d error: %s", req.TsxId, req.Op, backend, err)
		rsp := &api.Response{
			Op:      "rsp",
			Status:  http.StatusInternalServerError,
			Message: err.Error(),
			TsxId:   req.TsxId,
		}
		return rsp
	}
	return rsp
}

func badRequest(req *api.Request, reason string) *api.Response {
	log.Warningf("[%s:%s], bad request: %s", req.TsxId, req.Op, reason)
	return &api.Response{
		Op:      "rsp",
		Status:  http.StatusBadRequest,
		Message: reason,
		TsxId:   req.TsxId,
	}
}
